package com.tanhua.admin.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tanhua.admin.mapper.AnalysisMapper;
import com.tanhua.dubbo.api.UserApi;
import com.tanhua.model.domain.Analysis;
import com.tanhua.model.domain.User;
import com.tanhua.model.vo.DashboardVO;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;

/**
 * @author Xiao
 * @version 1.0
 * @description
 * @date 2021/11/3
 */
@Service
public class DashboardService {
    @Autowired
    private AnalysisMapper analysisMapper;
    @DubboReference
    private UserApi userApi;

    /**
     * 后台面板-新增、活跃用户、次日留存率
     * @param sd   开始时间
     * @param ed   结束时间
     * @param type 101(新增) / 102(活跃用户) / 103(次日留存率)
     * @return
     */
    public Map<String, Object> usersDashboard(String sd, String ed, Integer type) {
        try {
            Map<String, Object> resMap = new HashMap<>();
            Date thisSdDate = DateUtil.date(Long.parseLong(sd)).toSqlDate();
            Date thisEdDate = DateUtil.date(Long.parseLong(ed)).toSqlDate();
            long yearMilliSeconds = 365L * 24 * 60 * 60 * 1000;
            long dayMilliSeconds = 24 * 60 * 60 * 1000;
            Date lastSdDate = DateUtil.date(Long.parseLong(sd) - yearMilliSeconds).toSqlDate();
            Date lastEdDate = DateUtil.date(Long.parseLong(ed) - yearMilliSeconds).toSqlDate();
            List<Object> thisYear = new ArrayList<>();
            List<Object> lastYear = new ArrayList<>();
            List<Analysis> thisAnalyses = getAnalysisList(thisSdDate, thisEdDate);
            List<Analysis> lastAnalyses = getAnalysisList(lastSdDate, lastEdDate);
            int days = Math.toIntExact(
                    (thisEdDate.getTime() - thisSdDate.getTime()) / dayMilliSeconds);

            for (int i = 0; i <= days; i++) {
                Date thisDate =
                        DateUtil.date(Long.parseLong(sd) + i * dayMilliSeconds).toSqlDate();
                Date lastDate =
                        DateUtil.date(thisDate.getTime() - yearMilliSeconds).toSqlDate();
                Map<String, Object> thisYearMap = getYearMap(type, dayMilliSeconds, thisAnalyses, thisDate);
                Map<String, Object> lastYearMap = getYearMap(type, dayMilliSeconds, lastAnalyses, lastDate);
                thisYear.add(thisYearMap);
                lastYear.add(lastYearMap);
            }

            resMap.put("thisYear", thisYear);
            resMap.put("lastYear", lastYear);
            return resMap;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将title和amount封装到thisYear/lastYear
     * @param type
     * @param dayMilliSeconds
     * @param analyses
     * @param date
     * @return
     */
    private Map<String, Object> getYearMap(Integer type, long dayMilliSeconds, List<Analysis> analyses, Date date) {
        Map<String, Object> yearMap = new HashMap<>();
        String title = date.toString().substring(5);
        yearMap.put("title", title);
        Integer amount = 0;
        for (Analysis analysis : analyses) {
            String recordDateStr = DateUtil.date(analysis.getRecordDate()).toSqlDate().toString();
            String dateStr = date.toString();
            if (dateStr.equals(recordDateStr)) {
                switch (type) {
                    case 101 :{
                        amount = analysis.getNumRegistered();
                        break;
                    }
                    case 102 :{
                        amount = analysis.getNumActive();
                        break;
                    }
                    case 103 :{
                        Date yesterDayDate =
                                DateUtil.date(date.getTime() - dayMilliSeconds).toSqlDate();
                        Analysis yesterDayAnalysis = getAnalysis(yesterDayDate);
                        Integer todayRetention1d = analysis.getNumRetention1d();
                        if (ObjectUtil.isNotEmpty(yesterDayAnalysis) &&
                                yesterDayAnalysis.getNumRegistered() != 0) {
                            Integer yesterdayRegister = yesterDayAnalysis.getNumRegistered();
                            double registersTodayRate =
                                    (double) (todayRetention1d - yesterdayRegister) / yesterdayRegister * 100;
                            BigDecimal bigDecimal = BigDecimal.valueOf(registersTodayRate)
                                    .setScale(0, BigDecimal.ROUND_HALF_DOWN);
                            amount = Integer.valueOf(bigDecimal.toString());
                        } else {
                            amount = todayRetention1d * 100 ;
                        }
                        break;
                    }
                    default:
                        break;
                }
                break;
            }
        }
        yearMap.put("amount", amount);
        return yearMap;
    }

    /**
     * 查询时间范围内的统计数据
     * @param sdDate
     * @param edDate
     * @return
     */
    private List<Analysis> getAnalysisList(Date sdDate, Date edDate) {
        LambdaQueryWrapper<Analysis> wrapper = new LambdaQueryWrapper<>();
        wrapper.between(Analysis::getRecordDate, sdDate, edDate);
        return analysisMapper.selectList(wrapper);
    }

    /**
     * 根据日期查询当日记录数据
     * @param date
     */
    private Analysis getAnalysis(Date date) {
        LambdaQueryWrapper<Analysis> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Analysis::getRecordDate, date);
        return analysisMapper.selectOne(wrapper);
    }

    /**
     * 后台面板-概要统计信息
     * @return
     */
    public DashboardVO dashboardSummary() {
        try {
            //累计用户
            DashboardVO dashboardVO = new DashboardVO();
            List<User> list = userApi.list();
            if (CollUtil.isNotEmpty(list)) {
                dashboardVO.setCumulativeUsers(Math.min(list.size(), 500));
            }
            //过去30天活跃用户、过去7天活跃用户
            Date today = DateUtil.date().toSqlDate();
            Date week = DateUtil.lastWeek().toSqlDate();
            Date month = DateUtil.lastMonth().toSqlDate();
            Integer activePassWeek = analysisMapper.getActivePassDate(week, today);
            Integer activePassMonth = analysisMapper.getActivePassDate(month, today);
            if (ObjectUtil.isNotEmpty(activePassWeek) && ObjectUtil.isNotEmpty(activePassMonth)) {
                dashboardVO.setActivePassWeek(Math.min(activePassWeek, 500));
                dashboardVO.setActivePassMonth(Math.min(activePassMonth, 500));
            }
            //今日新增用户、今日登录次数、今日活跃用户
            LambdaQueryWrapper<Analysis> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Analysis::getRecordDate, today);
            Analysis analysis = analysisMapper.selectOne(wrapper);
            Integer newUsersToday = 0;
            Integer loginTimesToday = 0;
            Integer activeUsersToday = 0;
            if (ObjectUtil.isNotEmpty(analysis)) {
                newUsersToday = analysis.getNumRegistered();
                loginTimesToday = analysis.getNumLogin();
                activeUsersToday = analysis.getNumActive();
                //今日新增用户
                dashboardVO.setNewUsersToday(Math.min(newUsersToday, 500));
                //今日登录次数
                dashboardVO.setLoginTimesToday(Math.min(analysis.getNumLogin(), 500));
                //今日活跃用户
                dashboardVO.setActiveUsersToday(Math.min(analysis.getNumActive(), 500));
            }
            //今日新增用户涨跌率、今日登录次数涨跌率、今日活跃用户涨跌率
            Date yesterday =DateUtil.yesterday().toSqlDate();
            wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Analysis::getRecordDate, yesterday);
            analysis = analysisMapper.selectOne(wrapper);
            Integer newUsersYesterday = 0;
            Integer loginTimesYesterday = 0;
            Integer activeUsersYesterday = 0;
            if (ObjectUtil.isNotEmpty(analysis)) {
                //昨日新增用户、昨日登录次数、昨日活跃用户
                newUsersYesterday = analysis.getNumRegistered();
                loginTimesYesterday = analysis.getNumLogin();
                activeUsersYesterday = analysis.getNumActive();
                //今日新增用户涨跌率
                double newUsersTodayRate =
                        (double)(newUsersToday - newUsersYesterday) / newUsersYesterday * 100;
                BigDecimal bigDecimal = BigDecimal.valueOf(newUsersTodayRate)
                        .setScale(0, BigDecimal.ROUND_HALF_DOWN);
                dashboardVO.setNewUsersTodayRate(Integer.valueOf(bigDecimal.toString()));
                //今日登录次数涨跌率
                double loginTimesTodayRate =
                        (double)(loginTimesToday - loginTimesYesterday) / loginTimesYesterday * 100;
                bigDecimal = BigDecimal.valueOf(loginTimesTodayRate)
                        .setScale(0, BigDecimal.ROUND_HALF_DOWN);
                dashboardVO.setLoginTimesTodayRate(Integer.valueOf(bigDecimal.toString()));
                //今日活跃用户涨跌率
                double activeUsersTodayRate =
                        (double)(activeUsersToday - activeUsersYesterday) / activeUsersYesterday * 100;
                bigDecimal = BigDecimal.valueOf(activeUsersTodayRate)
                        .setScale(0, BigDecimal.ROUND_HALF_DOWN);
                dashboardVO.setActiveUsersTodayRate(Integer.valueOf(bigDecimal.toString()));
            } else {
                dashboardVO.setNewUsersTodayRate(newUsersToday * 100);
                dashboardVO.setLoginTimesTodayRate(loginTimesToday * 100);
                dashboardVO.setActiveUsersTodayRate(activeUsersToday * 100);
            }
            return dashboardVO;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
